import { runTimeTpl } from '@/utils';
import { types as t } from '@babel/core';
import generator from '@babel/generator';
import { parse } from '@babel/parser';
import traverse, { NodePath } from '@babel/traverse';
import { CallExpression } from '@babel/types';
import fs from 'node:fs';
import path from 'node:path';
import type { Plugin } from 'vite';
import  {tpl} from './runtime'
const transformsKeyDefault = {};
type TransformsKeyProps = { key: string; namespace?: string; message?: string };

type TransformsKeyType = Record<
  string,
  ({ key, message }: TransformsKeyProps) => string
>;

export default (options?: {
  savePath?: string;
  includes?: (id: string) => boolean;
  initValues?: Record<string, string>;
  transformsKey?: TransformsKeyType;
}): Plugin => {
  const {
    savePath = path.join(process.cwd(), './src/.qps/i18n.json'),
    includes = () => true,
    initValues,
    transformsKey: optionsTransformsKey,
  } = options || {};

  const transformsKey = {
    ...transformsKeyDefault,
    ...optionsTransformsKey,
  } as TransformsKeyType;
  const map: Record<string, string> = {};
  let count = 0;

  return {
    name: 'i18n',
    enforce:'pre',
    configResolved(){
       runTimeTpl({
            name:'plugin-i18n',
            fileName: 'runtime.ts',
            content:tpl()
       })
    },
    async transform(code: string, id: string) {
      const reg =
        /i18n\((?:\s*(?:'[^']*?'|"[^"]*?"|{[^}]*?})?\s*(?:,\s*(?:'[^']*?'|"[^"]*?"|{[^}]*?}))?\s*)\)/;
      if (id.includes('/src/') && reg.test(code) && includes?.(id)) {
        const ast = parse(code, {
          sourceType: 'module',
          plugins: ['jsx'],
        }) as any;

        traverse.default(ast, {
          CallExpression(path: NodePath<CallExpression>): void {
            const callee = path.node.callee;
            if (callee.type === 'Identifier' && callee.name === 'i18n') {
              count += 1;
              const firstArg = path.node.arguments[0];
              const lastArg = path.node.arguments[1];
              if (firstArg && t.isStringLiteral(firstArg)) {
                let value = firstArg.value;
                let key = transformKey(value);
                const valueTrim = value.trim();
                // 处理 i18n('1.2.3', 'test')
                if (/^\{[\s\w]*\}$/.test(valueTrim)) {
                  key = transformNamespaceKey({ key, namespace: 'var' });
                }

                if (lastArg && t.isStringLiteral(lastArg)) {
                  value = lastArg.value;
                }

                const expressionArgument = createObjectExpression(key, value);
                path.node.arguments[0] = expressionArgument;
                map[key] = value;

                // 处理 i18n({ id: '1.2.3',defaultMessage: 'test' })
              } else if (firstArg && t.isObjectExpression(firstArg)) {
                const obj = (firstArg.properties as Array<any>).reduce(
                  (pre: any, next: any) => {
                    if (
                      t.isObjectProperty(next) &&
                      t.isIdentifier(next.key) &&
                      t.isStringLiteral(next.value)
                    ) {
                      pre[next.key.name] = next.value.value;
                    } else {
                      pre[next.key.name] = '';
                    }
                    return pre;
                  },
                  {},
                );

                if (obj.id) {
                  map[obj.id] = obj.message || obj.defaultMessage; // 使用空字符串作为默认值，以防defaultMessage不存在
                }

                if (obj.message) {
                  let key = transformKey(obj.message);
                  const namespace = obj.namespace;

                  if (namespace && transformsKey?.[namespace]) {
                    key = transformsKey[namespace]?.({
                      key,
                      message: obj.message,
                    });
                  } else if (namespace) {
                    key = transformNamespaceKey({ key, namespace });
                  } else {
                    const valueTrim = obj.message.trim();
                    if (/^\{[\s\w]*\}$/.test(valueTrim)) {
                      key = transformNamespaceKey({ key, namespace: 'var' });
                    }
                  }

                  const expressionArgument = createObjectExpression(
                    key,
                    obj.message,
                  );
                  path.node.arguments[0] = expressionArgument;
                  map[key] = obj.message || '';
                }
              }
            }
          },
        });

        const { code: resultCode } = generator.default(ast);

        return {
          code: resultCode,
          map: null,
        };
      }
    },
    buildEnd() {
      if (Object.keys(map).length) {
        if (initValues && Object.keys(initValues).length) {
          const merge = Object.keys(map).reduce((pre: any, next: string) => {
            if (
              initValues?.[next] === null ||
              initValues?.[next] === undefined
            ) {
              pre[next] = map?.[next] || '';
            }

            return pre;
          }, {});

          fs.writeFileSync(
            path.join(path.dirname(savePath), '/i18n-merge.json'),
            JSON.stringify(merge),
          );
        }
      }

      fs.writeFileSync(savePath, JSON.stringify(map));

      console.log(`[vite-plugin-i18n] ${count} function i18n`);
    },
  };
};

function createObjectExpression(key: string, value: any) {
  return t.objectExpression([
    t.objectProperty(t.identifier('id'), t.stringLiteral(key)),
    t.objectProperty(t.identifier('defaultMessage'), t.stringLiteral(value)),
  ]);
}

function transformNamespaceKey({ key, namespace }: TransformsKeyProps): string {
  if (namespace) {
    return `${namespace}:${key}`;
  }

  return key;
}
const transformKey = (value: string): string => {
  return value
    .trim()
    .replace(/^[0-9]{1,10}\.\s*/, '')
    .replaceAll(/[!,;'\\{\\}\\?]/g, '')
    .replaceAll(/[)(]/g, '')
    .replace(/\s+|\s*\.\s*/g, '.')
    .replace(/\.+$/, '')
    .toLowerCase();
};
